#ifndef MTL_FUTURE_HPP
#define MTL_FUTURE_HPP

#include "thread.hpp"
#include "mutex.hpp"
#include "condition_variable.hpp"
#include "memory.hpp"

namespace mtl {

  class runnable {
  public:
    virtual ~runnable() {}
    virtual void run() = 0;
  };

  template<class _Bind>
  class runnable_impl {
  public:
    explicit runnable_impl(_Bind b) : m_bind(b) {}
    void run() { m_bind(); }
  private:
    _Bind m_bind;
  };

  template<class R> class callable {
  public:
    typedef R result_type;
  public:
    virtual ~callable() {}
    virtual result_type call() = 0;
  };

  template<class R> class runnable_adapter : public callable<R> {
  public:
    typedef typename callable<R>::result_type result_type;
  public:
    runnable_adapter(shared_ptr<runnable> r, R value)
      : m_run(r), m_value(value)
    {}
    result_type call() {
      m_run->run();
      return m_value;
    }
  private:
    shared_ptr<runnable>  m_run;
    R m_value;
  };

  template<class R> class future {
  public:
    typedef R result_type;
  public:
    virtual ~future() {}
    virtual bool cancel(bool may_interrupt) = 0;
    virtual bool is_cancelled() const = 0;
    virtual bool is_done() const = 0;
    virtual result_type get() = 0;
    virtual void attach_callback(function<void (future<result_type>*)> f) = 0;
  };

  template<class R>
  class runnable_future : public runnable, public future<R> {
    // implementation nothing
  };

  namespace {
  }

  template<class _Bind, class _Result>
  class future_task : public runnable_future<_Result> {
  protected:
    enum task_state {
      WAITING   = 0,
      RUNNING   = 1,
      RAN       = 2,
      CANCELLED = 4,
    };

    bool notify_finished(task_state state) {
      lock_guard<mutex> lock(m_state_mtx);
      if (m_state == state) {
        return false;
      }
      m_state = state;
      m_finished_cond.notify_all();
      return true;
    }

    template<class _B, class _R> class task_impl {
    public:
      task_impl(future_task<_B, _R>* peer)
        : m_peer(peer)
      {}
      void run() {
        {
          lock_guard<mutex> lock(m_peer->m_state_mtx);
          m_peer->m_state = RUNNING;
        }
        {
          lock_guard<mutex> lock(m_peer->m_runner_mtx);
          m_peer->m_runner = this_thread::get_id();
        }
        try {
          m_result = m_peer->m_bind();
        } catch (const std::exception& e) {
          // TODO
        }
        m_peer->notify_finished(RAN);
        m_peer->m_done->call(m_peer);
      }
      _R get() {
        {
          lock_guard<mutex> lock(m_peer->m_state_mtx);
          while (!m_peer->is_done()) {
            m_peer->m_finished_cond.wait(lock);
          }
        }
        // TODO exception
        return m_result;
      }
    private:
      future_task<_B, _R>*  m_peer;
      _R  m_result;
    };

    template<class _B> class task_impl<_B, void> {
    public:
      task_impl(future_task<_B, void>* peer)
        : m_peer(peer)
      {}
      void run() {
        {
          lock_guard<mutex> lock(m_peer->m_state_mtx);
          m_peer->m_state = RUNNING;
        }
        {
          lock_guard<mutex> lock(m_peer->m_runner_mtx);
          m_peer->m_runner = this_thread::get_id();
        }
        try {
          m_peer->m_bind();
        } catch (const std::exception& e) {
          // TODO
        }
        m_peer->notify_finished(RAN);
        m_peer->m_done->call(m_peer);
      }
      void get() {
        {
          lock_guard<mutex> lock(m_peer->m_state_mtx);
          while (!m_peer->is_done()) {
            m_peer->m_finished_cond.wait(lock);
          }
        }
        // TODO exception
      }
    private:
      future_task<_B, void>*  m_peer;
    };

    friend class task_impl<_Bind, _Result>;

    class callback_handler {
    public:
      virtual ~callback_handler() {}
      virtual void call(future<_Result>* f) = 0;
      static void null_func(future<_Result>* f) {}
    };
    template<class _H> class callback_handler_impl : public callback_handler {
    public:
      explicit callback_handler_impl(_H h) : m_h(h) {}
      void call(future<_Result>* f) { m_h(f); }
    private:
      _H  m_h;
    };
    template<class _H> callback_handler* create_callback_handler(_H h) {
      return new callback_handler_impl<_H>(h);
    }
  public:
    explicit future_task(_Bind b)
      : m_bind(b),
        m_done(create_callback_handler(callback_handler::null_func)),
        m_state(WAITING),
        m_inner(this)
    {}

    void run() {
      m_inner.run();
    }

    _Result get() {
      return m_inner.get();
    }

    bool cancel(bool may_interrupt) {
      if (is_done()) {
        return false;
      }
      if (!notify_finished(CANCELLED)) {
        // すでにキャンセル済みだった
        return false;
      }
      if (may_interrupt) {
        lock_guard<mutex> lock(m_runner_mtx);
        thread::interrupt(m_runner);  // 失敗覚悟で call
      }
      m_done->call(this);
      return true;
    }

    bool is_cancelled() const {
      return m_state == CANCELLED;
    }

    bool is_done() const {
      return (m_state & (RAN | CANCELLED)) != 0;
    }

    void attach_callback(function<void (future<_Result>*)> h) {
      {
        lock_guard<mutex> lock(m_done_mtx);
        m_done.reset(create_callback_handler(h));
      }
      if (is_done()) {
        m_done->call(this);
      }
    }
  private:
    _Bind               m_bind;

    shared_ptr<callback_handler>  m_done;
    mutex               m_done_mtx;

    thread::id  m_runner;
    mutex               m_runner_mtx;

    volatile task_state m_state;
    mutex               m_state_mtx;
    condition_variable  m_finished_cond;

    task_impl<_Bind, _Result> m_inner;
  };

}
#endif
